module Explode.PauseMenuState;
private import Explode.Stage;
private import Explode.Player;
private import Explode.GameConfig;
private import Explode.GameState;
private import Form.MenuItem;
private import State.IState;
private import Input.KeyCode;
private import Input.GamePad;
private import Graphics.IGraphics;
private import System.Console;
private import Sound.Manager;
private import std.file;
private import std.string;
private import std.format;
private import std.math;

/**
*	PAUSE画面用
*/
private class PauseMenuState : BasicState {
	public const int GAME        = 0;
	public const int GIVEUP      = 1;
	public const int STAGESELECT = 2;
	public const int TITLE       = 3;
	protected int _returnValue = 0;
	public int returnValue(){return _returnValue;}
	protected int _selectedValue = 0;
	public int selectedValue(){return _selectedValue;}

	protected const int WIDTH = 320 , HEIGHT = 400;

	private class Background : IDrawingEntry{
		public float x(){return 0;}
		public float y(){return 0;}
		public float z(){return 200;}
		public bool drawingEntry( IGraphics g ){
			return g.addDrawingEntry( this );
			
		} //
		public bool draw( IGraphics g ){
			g.setViewport( 0 , 0, WIDTH , HEIGHT );
			g.drawFilledRect( 0 , 0 , WIDTH , HEIGHT , Color.create(0x88000000u) );
			if ( _frameCnt / 30 % 3 > 0 ) {
			string txt = "P A U S E";
			int ww = 32 , hh = 64;
				g.drawText( txt , ( WIDTH - ww * txt.length )/2 , 70 , Color.create( 0x66ffffffu ) , ww , hh );

				g.drawText( txt , ( WIDTH - ww * txt.length )/2 , 50 , Color.create( 0xccff0000u ) , ww , hh );
			}

			g.drawFilledRect( 64 , HEIGHT / 2 + 80 , WIDTH - 128 ,  96 , Color.create( 0x330000ffu ) , 0 );

			if ( _activeItem is _stageMenu )  {
				_pstage.draw( g );

				int ww = 8 , hh = 16;
				string btxt = format("BEST TIME %06d" , _bestTime );
				if ( _bestTime > 0 )
					g.drawText( btxt 
							, ( WIDTH - ww * btxt.length ) / 2 
							, (HEIGHT - hh) / 2 + 65
							, Color.create( 0xccffff00u ) , ww , hh );
			}
			return true;
		} //
	} //


	private GameStage _stage;
	private GameParam _param;
	private IState _caller;
	private Player _player;
	private int _frameCnt;
	protected IGamePad _pad;
	protected IMenuItem[] _items; 
	protected MenuItem _returnMenu , _giveupMenu , _scrollMenu , _titleMenu;
	protected EnumMenuItem _stageMenu;
	
	protected GameState _gameState;
	protected IMenuItem _activeItem ;
	protected IState _nextState ;
	protected IState _scroller;
	protected IGraphics _graphics;

	protected string[] _stageValues;
	protected Background _background;
	protected PreviewStage _pstage;
	protected int _bestTime = -1;
	


	public this( GameState state , GameParam param , IGamePad pad , IGraphics g ){
		_gameState = state;
		_param     = param;
		_pad   = pad;
		_graphics  = g;
	} //

	public IState call( IState caller ) {
		_caller = caller;
		_nextState = this;
		_pad.reset();
		_returnValue = 0;
		initMenu();
		_frameCnt = 0;
		_stage = _gameState.stage;
		SoundManager.background.pause();
		return this;
	} //
	
	private void initMenu(){
		if ( _returnMenu is null ) {
			Console.write("Create PauseMenu...");
			_background = new Background();
			_pstage		= new PreviewStage( _graphics );
			_scroller	= new PauseScrollState( _gameState , _param , _pad , _graphics );
			_returnMenu  = new MenuItem("RETURN TO GAME");
			_giveupMenu  = new MenuItem("GIVEUP");
			_scrollMenu  = new MenuItem("SCROLL");
			_stageMenu   = new EnumMenuItem("STAGE SELECT    ");
			_titleMenu   = new MenuItem("RETURN TO TITLE");
			
			_items = [	 _giveupMenu
						,_scrollMenu
						,_stageMenu
						,_titleMenu
						,_returnMenu
					];
			MenuItemConnector.connectVertical( _items );
			_activeItem = _returnMenu.activate();
			
			int xx = WIDTH / 2 , yy = HEIGHT / 2 + 60;
			_giveupMenu.moveTo(  xx , yy +=  20 );
			_scrollMenu.moveTo(  xx , yy +=  20 );
			_stageMenu.moveTo(   xx , yy +=  20 );
			_titleMenu.moveTo(   xx , yy +=  20 );
			_returnMenu.moveTo(  xx , yy +=  20 );
		
			_giveupMenu.setSelectHandler( &onGiveupMenuSelect );
			_scrollMenu.setSelectHandler( &onScrollMenuSelect );
			_stageMenu.setSelectHandler(  &onStageMenuSelect );
			_stageMenu.setChangedHandler(  &onStageMenuChange );
			_titleMenu.setSelectHandler(  &onTitleMenuSelect );
			_returnMenu.setSelectHandler( &onReturnMenuSelect );
			for ( int i = 1 ; i <= GameConfig.stageLength ; i++ ) {
				_stageValues ~= std.string.toString(i);
			}
			Console.writeln("OK");
		}
		_stageMenu.setValues( "STAGE  " , _stageValues[0..GameConfig.bestStage] , _param.stageNum -1 );
		setStageNumber( _param.stageNum  );
		
	} //

	int _cntY = 0, _cntX = 0;
	public IState action() {
		_frameCnt ++;
		foreach ( int i , IMenuItem item ; _items  ) {
			item.action();
		} //
		if ( _frameCnt > 30 ) {
			if ( _pad.y != 0 )  _cntY++; 
			else					_cntY = 0;
			if ( _pad.x != 0 )  _cntX++;
			else					_cntX = 0;
			if ( _cntY % 10 == 1 ) {
				_activeItem = ( _pad.y > 0 ) ? _activeItem.down  : _activeItem.up;
			}
			if ( _cntX % 10 == 1  || ( _cntX > 60 && _cntX % 3 == 1 ) ) {
				_activeItem = ( _pad.x > 0 ) ? _activeItem.right : _activeItem.left;
			}
		}
		if ( _activeItem is _stageMenu ) {
				 _pstage.action();
			if ( _pstage.frameCnt < 10 ) _pstage.zoomOut();
		}

		if ( _pad.getButton( 0 ) != 0 ) {
			_activeItem.select();
		}

		return _nextState;
	} //

	public IState draw( IGraphics g ) {
		foreach ( int i , IMenuItem item ; _items  ) {
			item.drawingEntry( g );
		} //
		_stage.setViewport( g ) ;
		_stage.drawingEntry( g );
		_background.drawingEntry( g );
		g.draw();
		return this;
	} //

	public int setStageNumber( int num ) {
		string file   = format("stage%04d.txt" , num );
		string src	  = GameConfig.stageDir ~ "/" ~ file;
		string replay = GameConfig.stageDir ~ "/replay/" ~ file;
		string defSrc = GameConfig.stageDir ~ "/stage0001.txt";
		if ( !exists( src ) ) {
			src = defSrc;
			num = 1;
			if( !exists( src ) ) {
				return false;
			}
		}
		_pstage.loadStageFromFile( src );
		for ( int i = 0 ; i < 30 ; i++ ) _pstage.zoomOut();
		if ( exists( replay ) ) {
			string  head = split( cast(string)read( replay ) , "[body]" )[0];
			string[] dat = split( head , ",");
			_bestTime = cast(int)atoi( dat[0] );
		} else
			_bestTime = -1;
		return num;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = -1;
				break;
			case KeyCode.DOWN:
				_pad.y =  1;
				break;
			case KeyCode.LEFT:
				_pad.x = -1;
				break;
			case KeyCode.RIGHT:
				_pad.x =  1;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 1 );
				break;
			case KeyCode.SPACE:
				_nextState = _gameState.call( this );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = 0;
				break;
			case KeyCode.DOWN:
				_pad.y = 0;
				break;
			case KeyCode.LEFT:
				_pad.x = 0;
				break;
			case KeyCode.RIGHT:
				_pad.x = 0;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 0 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _pad.x = ( delta > 0 ) ? 1 : -1;
			else			 _pad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _pad.y = ( delta > 0 ) ? 1 : -1;
			else			 _pad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		int bm = GameConfig.getButtonMap( button );
		if ( bm == 9 ) _nextState = _gameState.call( this );
		else
		_pad.setButton( GameConfig.getButtonMap( button ) , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_pad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //


	public void onReturnMenuSelect( IMenuItem im ) {
		_nextState = _gameState.call( this );
		Console.writeln("RETURN TO  GAME!");
	} //

	public void onGiveupMenuSelect( IMenuItem im ) {
		_returnValue = GIVEUP;
		_nextState = _gameState.call( this );
		Console.writeln("GIVEUP!");
	} //

	public void onScrollMenuSelect( IMenuItem im ) {
		_returnValue = GAME;
		_nextState = _scroller.call( this );
		Console.writeln("SCROLL!");
	} //

	public void onStageMenuSelect( IMenuItem im ) {
		_returnValue = STAGESELECT;
		_selectedValue = cast(int)atoi( _stageMenu.value );
		_nextState = _gameState.call( this );
		Console.writeln("RETURN TO  GAME!");
	} //
	public void onStageMenuChange( EnumMenuItem im , string selectedValue , int selectedIndex ) {
		int num = cast(int)atoi( selectedValue );
		setStageNumber( num );
	} //

	
	public void onTitleMenuSelect(IMenuItem im){
		_returnValue = TITLE;
		_nextState = _gameState.call( this );
		Console.writeln("GO TO TITLE!");
	}


} //



/**
*	PAUSE中の画面スクロール用
*/
private class PauseScrollState : BasicState {
	public const int CANCEL    = 0;
	protected int _returnValue = 0;
	public int returnValue(){return _returnValue;}

	protected const int WIDTH = 320 , HEIGHT = 400;

	private class Background : IDrawingEntry{
		public float x(){return 0;}
		public float y(){return 0;}
		public float z(){return 200;}
		public bool drawingEntry( IGraphics g ){
			return g.addDrawingEntry( this );
		} //
		public bool draw( IGraphics g ) {
			_stage.setViewport( g );
			_pointer.draw( g );
			g.setViewport( 0 , 0, WIDTH , HEIGHT );
			g.drawFilledRect( 0 , 0 , WIDTH , HEIGHT , Color.create(0x88000000u) );
			if ( _frameCnt / 30 % 3 > 0 ) {
				string txt = "PAUSE";
				int ww = 16 , hh = 32;
				g.drawText( txt , ( WIDTH - ww * txt.length )/2 , (HEIGHT - hh)/ 2 
				, Color.create( 0x88ff0000u ) , ww , hh );
			}
			return true;
		} //
	} //
	
	private class Pointer : IDrawingEntry {
		public float _x , _y , _z;
		public float x(){ return _x;}
		public float y(){ return _y;}
		public float z(){ return 180;}
		public bool drawingEntry( IGraphics g ){
			return g.addDrawingEntry( this );
		} //
		public bool draw( IGraphics g ) {
			g.drawRect( _x , _y , 16 , 16 , Color.create( 0x88ffffff ) );
			return true;
		} //
		public void moveTo( float x , float y ) {
			if ( x < 0 ) x = 0;
			if ( y < 0 ) y = 0;
			int sw = ( _stage.cols - 1 )* Blocks.WIDTH , sh = ( _stage.rows - 1 )* Blocks.HEIGHT;
			if ( x > sw ) x = sw;
			if ( y > sh ) y = sh;
			_x = x;
			_y = y;
		} //
		public void moveBy( float x , float y) {
			moveTo( _x + x , _y + y );
		}
	} //


	protected BaseStage _stage;
	protected GameParam _param;
	protected int _frameCnt;
	protected IGamePad _pad;
	protected IMenuItem[] _items; 

	protected IState _nextState , _caller ;
	protected IGraphics _graphics;
	
	protected bool _returnFlag ;
	protected float _startX , _startY;
	protected Pointer _pointer;
	protected GameState _gameState;
	protected int _zoom;

	protected Background _background;

	public this( GameState state , GameParam param , IGamePad pad , IGraphics g ){
		_gameState = state;
		_param     = param;
		_pad   = pad;
		_graphics  = g;
	} //

	public IState call( IState caller ) {
		_caller = caller;
		_nextState = this;
		_pad.reset();
		_returnValue = 0;
		_frameCnt = 0;
		_zoom = 0;
		initMenu();

		_stage  = _gameState.stage;
		_startX = _stage.player.x;
		_startY = _stage.player.y;
		_pointer.moveTo( _startX , _startY );
		_returnFlag = false;
		return this;
	} //
	
	private void initMenu(){
		if ( _background is null ) {
			Console.write("Create PauseScrollMenu...");
			_background = new Background();
			_pointer = new Pointer();
		}
	} //

	int _cntY = 0, _cntX = 0;
	public IState action() {
		_frameCnt ++;
		if ( _zoom > 0 ) _stage.zoomIn();
		else if ( _zoom < 0 ) _stage.zoomOut();

		if ( !_returnFlag ) {
			if ( _pad.x != 0 ) {
				_pointer.moveBy( _pad.x * 4 , 0 );
			} //
			if ( _pad.y != 0 ) {
				_pointer.moveBy( 0 , _pad.y * 4 );
			} //
			if ( _pad.getButton( 0 ) != 0 || _pad.getButton( 1 ) != 0 ) {
				_pointer.moveTo( _startX , _startY );
			}
		} else {
			float xd = ( _pointer.x - _startX );
			float yd = ( _pointer.y - _startY );
			_pointer.moveBy( -xd / 10 , -yd / 10 );
			if ( abs( xd ) < Blocks.WIDTH && abs( yd ) < Blocks.HEIGHT ) {
				return _caller.call( this );
			}
		}
		_stage.setScrollPoint( _pointer );
		return _nextState;
	} //

	public IState draw( IGraphics g ) {
		_stage.drawingEntry( g );
		_background.drawingEntry( g );
		_stage.setViewport( g );
		g.draw();
		return this;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = -1;
				break;
			case KeyCode.DOWN:
				_pad.y =  1;
				break;
			case KeyCode.LEFT:
				_pad.x = -1;
				break;
			case KeyCode.RIGHT:
				_pad.x =  1;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 1 );
				break;
			case KeyCode.SPACE:
				_returnFlag = true;
				break;
			case KeyCode.PAGEDOWN:
				_zoom = -1;
				break;
			case KeyCode.PAGEUP:
				_zoom =  1;
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = 0;
				break;
			case KeyCode.DOWN:
				_pad.y = 0;
				break;
			case KeyCode.LEFT:
				_pad.x = 0;
				break;
			case KeyCode.RIGHT:
				_pad.x = 0;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 0 );
				break;
			case KeyCode.PAGEDOWN:
				_zoom = 0;
				break;
			case KeyCode.PAGEUP:
				_zoom =  0;
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _pad.x = ( delta > 0 ) ? 1 : -1;
			else			 _pad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _pad.y = ( delta > 0 ) ? 1 : -1;
			else			 _pad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		int bm = GameConfig.getButtonMap( button );
		if ( bm == 9 ) _nextState = _gameState.call( this );
		else
		_pad.setButton( GameConfig.getButtonMap( button ) , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_pad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //


} //

